package com.stottlerhenke.simbionic.engine.core;
import com.stottlerhenke.simbionic.api.*;
import com.stottlerhenke.simbionic.common.*;
import com.stottlerhenke.simbionic.common.debug.*;
import com.stottlerhenke.simbionic.engine.SB_SingletonBook;
import com.stottlerhenke.simbionic.engine.debug.EEventType;


public class SB_FinalNode extends SB_Node {
  public SB_FinalNode(SB_BehaviorElement owner, int nodeID) {
    super(owner, nodeID);
  }

  public ENodeType GetNodeType() {
    return ENodeType.kFinalNode;
  }

  public String GetLogName() {
	  return "<Final Node>";
  }

  public void Execute(SB_Entity ent, SB_ExecutionFrame contextFrame, SB_SingletonBook book) throws SB_Exception {
    SB_EntityData state = ent.GetState();

    if(SIM_Constants.DEBUG_INFO_ON)
		book.getLogger().log("[" + ent.toString() + ",STK " + contextFrame.GetStackDepth() + "] BEHAVIOR:\tcompleted "
									+ contextFrame.GetBehavior().getName(),SB_Logger.BEHAVIOR);

    // keep track of whether this node has been applied
    contextFrame.SetNodeBeenInvoked(true);

    if( SIM_Constants.AI_DEBUGGER )
    {
    		// the eng_frame_completed message must be sent before the
				// eng_frame_current message generated by stack->Pop() to
				// avoid confusing the debug client in Step Over mode
        DMFieldMap fields = new DMFieldMap();
        fields.ADD_ID_FIELD("entity",  ent.GetId()._id );
        fields.ADD_INT_FIELD("frame", contextFrame.GetStackDepth() );
        contextFrame.GetDebugger().RecordEvent(EEventType.kEVT_FRAME_COMPLETED, fields);
    }

		SB_ExecutionStack stack = state.GetExecStack();
		int execMode = stack.getExecMode();

    // terminate execution of this behavior and pop the stack frame
		if( null == stack.Pop()) //In this case, flow of control went to the always node. We aren't actually done
			return;

    if (state.GetExecStack().getSize() == 0)
    {
        // this was the final node of the base-level behavior:  the entity is finished
        state.SetFinished(true);
        return;
    }

    // re-sync the terminated behavior's parameters with the calling behavior
    SB_ExecutionFrame parentFrame = contextFrame.GetParent();
    parentFrame.GetCurrNode().SyncBindings(ent,parentFrame,contextFrame);

    // notify the parent frame that its invoked behavior has completed
    if (contextFrame.IsInterrupt()) {
      // this is an interrupt behavior
      try{
        parentFrame.InterruptDone();
      }catch(SB_Exception e){

      }
    }
    else {
      // this is a normal invoked behavior
      parentFrame.SetInvokedDone(true);
    }
    
		// final nodes don't waste a clock tick (unless they are
		// in a one-tick behavior)
		if ((execMode & SB_BehaviorClass.kMODE_ONETICK) == 0)
		{
			state.SetDoAnotherTick(true);
		}
  }

}